msg_tool\scripts\circus\image/
crx.rs

1//! Circus Image File (.crx)
2use crate::ext::io::*;
3use crate::scripts::base::*;
4use crate::types::*;
5use crate::utils::img::*;
6use crate::utils::struct_pack::*;
7use anyhow::Result;
8use clap::ValueEnum;
9use clap::builder::PossibleValue;
10use msg_tool_macro::*;
11use overf::wrapping;
12use std::io::{Read, Seek, Write};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15/// Circus CRX Row Encoding Mode
16pub enum CircusCrxMode {
17    /// Encoding all rows with a fixed type.
18    Fixed(u8),
19    /// When importing, use origin mode; when creating, use best mode.
20    Auto,
21    /// Use origin mode for importing; when creating, fallback to best mode.
22    Origin,
23    /// Try to use the best mode for encoding.
24    Best,
25}
26
27impl Default for CircusCrxMode {
28    fn default() -> Self {
29        CircusCrxMode::Auto
30    }
31}
32
33impl CircusCrxMode {
34    /// Returns mode for importing.
35    pub fn for_importing(&self) -> Self {
36        match self {
37            CircusCrxMode::Auto => CircusCrxMode::Origin,
38            _ => *self,
39        }
40    }
41
42    /// Returns mode for creating.
43    pub fn for_creating(&self) -> Self {
44        match self {
45            CircusCrxMode::Auto => CircusCrxMode::Best,
46            CircusCrxMode::Origin => CircusCrxMode::Best,
47            _ => *self,
48        }
49    }
50
51    /// Checks if the mode is best.
52    pub fn is_best(&self) -> bool {
53        matches!(self, CircusCrxMode::Best)
54    }
55
56    /// Checks if the mode is origin.
57    pub fn is_origin(&self) -> bool {
58        matches!(self, CircusCrxMode::Origin)
59    }
60}
61
62impl ValueEnum for CircusCrxMode {
63    fn value_variants<'a>() -> &'a [Self] {
64        &[
65            CircusCrxMode::Fixed(0),
66            CircusCrxMode::Fixed(1),
67            CircusCrxMode::Fixed(2),
68            CircusCrxMode::Fixed(3),
69            CircusCrxMode::Fixed(4),
70            CircusCrxMode::Auto,
71            CircusCrxMode::Origin,
72            CircusCrxMode::Best,
73        ]
74    }
75
76    fn to_possible_value(&self) -> Option<PossibleValue> {
77        Some(match self {
78            CircusCrxMode::Fixed(0) => PossibleValue::new("0").help("Row type 0"),
79            CircusCrxMode::Fixed(1) => PossibleValue::new("1").help("Row type 1"),
80            CircusCrxMode::Fixed(2) => PossibleValue::new("2").help("Row type 2"),
81            CircusCrxMode::Fixed(3) => PossibleValue::new("3").help("Row type 3"),
82            CircusCrxMode::Fixed(4) => PossibleValue::new("4").help("Row type 4"),
83            CircusCrxMode::Auto => PossibleValue::new("auto")
84                .help("When importing, use origin mode, otherwise use best mode."),
85            CircusCrxMode::Origin => PossibleValue::new("origin")
86                .help("Use origin mode for importing. When creating, fallback to best mode."),
87            CircusCrxMode::Best => PossibleValue::new("best").help("Try to use the best mode."),
88            _ => return None,
89        })
90    }
91}
92
93#[derive(Debug)]
94/// Circus CRX Image Builder
95pub struct CrxImageBuilder {}
96
97impl CrxImageBuilder {
98    /// Creates a new instance of `CrxImageBuilder`.
99    pub const fn new() -> Self {
100        CrxImageBuilder {}
101    }
102}
103
104impl ScriptBuilder for CrxImageBuilder {
105    fn default_encoding(&self) -> Encoding {
106        Encoding::Cp932
107    }
108
109    fn build_script(
110        &self,
111        data: Vec<u8>,
112        _filename: &str,
113        _encoding: Encoding,
114        _archive_encoding: Encoding,
115        config: &ExtraConfig,
116        _archive: Option<&Box<dyn Script>>,
117    ) -> Result<Box<dyn Script>> {
118        Ok(Box::new(CrxImage::new(MemReader::new(data), config)?))
119    }
120
121    fn extensions(&self) -> &'static [&'static str] {
122        &["crx"]
123    }
124
125    fn script_type(&self) -> &'static ScriptType {
126        &ScriptType::CircusCrx
127    }
128
129    fn is_image(&self) -> bool {
130        true
131    }
132
133    fn is_this_format(&self, _filename: &str, buf: &[u8], buf_len: usize) -> Option<u8> {
134        if buf_len >= 4 && buf.starts_with(b"CRXG") {
135            return Some(255);
136        }
137        None
138    }
139
140    fn can_create_image_file(&self) -> bool {
141        true
142    }
143
144    fn create_image_file<'a>(
145        &'a self,
146        data: ImageData,
147        _filename: &str,
148        writer: Box<dyn WriteSeek + 'a>,
149        options: &ExtraConfig,
150    ) -> Result<()> {
151        CrxImage::create_image(data, writer, options)
152    }
153}
154
155#[derive(Clone, Debug, StructPack, StructUnpack)]
156struct Clip {
157    field_0: u32,
158    img_width: u16,
159    img_height: u16,
160    clip_offset_x: u16,
161    clip_offset_y: u16,
162    clip_width: u16,
163    clip_height: u16,
164}
165
166#[derive(Clone, Debug, StructPack, StructUnpack)]
167struct Header {
168    inner_x: u16,
169    inner_y: u16,
170    width: u16,
171    height: u16,
172    version: u16,
173    flags: u16,
174    bpp: u16,
175    mode: u16,
176    #[skip_pack_if(self.version != 3)]
177    #[skip_unpack_if(version != 3)]
178    #[pvec(u32)]
179    clips: Vec<Clip>,
180}
181
182#[derive(Clone, Debug)]
183enum CrxImageData {
184    RowEncoded(Vec<u8>),
185    IndexedV1 {
186        pixels: Vec<u8>,
187        stride: usize,
188        palette: Vec<u8>,
189        palette_format: PaletteFormat,
190        pixel_depth_bits: usize,
191    },
192    Direct(Vec<u8>),
193}
194
195impl CrxImageData {
196    fn is_row_encoded(&self) -> bool {
197        matches!(self, CrxImageData::RowEncoded(_))
198    }
199}
200
201/// Circus CRX Image
202pub struct CrxImage {
203    header: Header,
204    color_type: ImageColorType,
205    data: CrxImageData,
206    compress_level: u32,
207    keep_original_bpp: bool,
208    zstd: bool,
209    zstd_compression_level: i32,
210    row_type: CircusCrxMode,
211    canvas: bool,
212}
213
214impl std::fmt::Debug for CrxImage {
215    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
216        let data_info = match &self.data {
217            CrxImageData::RowEncoded(buf) => format!("row-encoded({})", buf.len()),
218            CrxImageData::IndexedV1 { pixels, .. } => {
219                format!("indexed-v1({})", pixels.len())
220            }
221            CrxImageData::Direct(buf) => format!("direct({})", buf.len()),
222        };
223        f.debug_struct("CrxImage")
224            .field("header", &self.header)
225            .field("color_type", &self.color_type)
226            .field("data", &data_info)
227            .finish()
228    }
229}
230
231impl CrxImage {
232    /// Creates a new `CrxImage` from the given data and configuration.
233    ///
234    /// * `data` - The reader to read the CRX image from.
235    /// * `config` - Extra configuration options.
236    pub fn new<T: Read + Seek>(data: T, config: &ExtraConfig) -> Result<Self> {
237        let mut reader = data;
238        let mut magic = [0; 4];
239        reader.read_exact(&mut magic)?;
240        if magic != *b"CRXG" {
241            return Err(anyhow::anyhow!("Invalid CRX image magic"));
242        }
243        let header: Header = reader.read_struct(false, Encoding::Utf8, &None)?;
244        if header.version == 0 || header.version > 3 {
245            return Err(anyhow::anyhow!(
246                "Unsupported CRX version: {}",
247                header.version
248            ));
249        }
250
251        let (color_type, data) = if header.version == 1 {
252            let width = usize::from(header.width);
253            let height = usize::from(header.height);
254            if width == 0 || height == 0 {
255                return Err(anyhow::anyhow!("CRX v1 image has zero dimensions"));
256            }
257
258            let bits_per_pixel = match header.bpp {
259                0 => 24usize,
260                1 => 32usize,
261                _ => 8usize,
262            };
263            if bits_per_pixel % 8 != 0 {
264                return Err(anyhow::anyhow!(
265                    "Unsupported bits per pixel {} for CRX v1",
266                    bits_per_pixel
267                ));
268            }
269            let pixel_size = bits_per_pixel / 8;
270            if pixel_size == 0 {
271                return Err(anyhow::anyhow!("Invalid pixel size for CRX v1 image"));
272            }
273
274            let row_bytes = width
275                .checked_mul(pixel_size)
276                .ok_or_else(|| anyhow::anyhow!("CRX v1 row size overflow"))?;
277            let stride = (row_bytes
278                .checked_add(3)
279                .ok_or_else(|| anyhow::anyhow!("CRX v1 stride overflow"))?)
280                & !3usize;
281            let output_len = stride
282                .checked_mul(height)
283                .ok_or_else(|| anyhow::anyhow!("CRX v1 buffer size overflow"))?;
284
285            let palette = if bits_per_pixel == 8 {
286                let raw_colors = usize::from(header.bpp);
287                Some((
288                    Self::read_v1_palette(&mut reader, raw_colors)?,
289                    PaletteFormat::Rgb,
290                ))
291            } else {
292                None
293            };
294
295            if (header.flags & 0x10) != 0 {
296                reader.read_u32()?; // stored compressed size, ignored for v1
297            }
298
299            let pixels = Self::unpack_v1(&mut reader, output_len)?;
300
301            if let Some((palette, palette_format)) = palette {
302                let data = CrxImageData::IndexedV1 {
303                    pixels,
304                    stride,
305                    palette,
306                    palette_format,
307                    pixel_depth_bits: bits_per_pixel,
308                };
309                (ImageColorType::Bgr, data)
310            } else {
311                let mut trimmed = Vec::with_capacity(
312                    row_bytes
313                        .checked_mul(height)
314                        .ok_or_else(|| anyhow::anyhow!("CRX v1 buffer size overflow"))?,
315                );
316                for row in 0..height {
317                    let start = row
318                        .checked_mul(stride)
319                        .ok_or_else(|| anyhow::anyhow!("CRX v1 row offset overflow"))?;
320                    let end = start
321                        .checked_add(row_bytes)
322                        .ok_or_else(|| anyhow::anyhow!("CRX v1 row slice overflow"))?;
323                    if end > pixels.len() {
324                        return Err(anyhow::anyhow!(
325                            "CRX v1 image data is shorter than expected"
326                        ));
327                    }
328                    trimmed.extend_from_slice(&pixels[start..end]);
329                }
330                let color_type = match bits_per_pixel {
331                    24 => ImageColorType::Bgr,
332                    32 => ImageColorType::Bgra,
333                    _ => ImageColorType::Bgr,
334                };
335                (color_type, CrxImageData::Direct(trimmed))
336            }
337        } else {
338            let color_type = if header.bpp == 0 {
339                ImageColorType::Bgr
340            } else if header.bpp == 1 {
341                ImageColorType::Bgra
342            } else {
343                return Err(anyhow::anyhow!("Unsupported CRX bpp: {}", header.bpp));
344            };
345            let compressed_size = if (header.flags & 0x10) == 0 {
346                let len = reader.stream_length()?;
347                (len - reader.stream_position()?) as u32
348            } else {
349                reader.read_u32()?
350            };
351            let compressed_data = reader.read_exact_vec(compressed_size as usize)?;
352            let uncompressed = if compressed_data.starts_with(&[0x28, 0xb5, 0x2f, 0xfd]) {
353                let mut decoder = zstd::Decoder::new(MemReaderRef::new(&compressed_data))?;
354                let mut decompressed_data = Vec::new();
355                decoder.read_to_end(&mut decompressed_data)?;
356                decompressed_data
357            } else {
358                let mut decompressed_data = Vec::new();
359                flate2::read::ZlibDecoder::new(MemReaderRef::new(&compressed_data))
360                    .read_to_end(&mut decompressed_data)?;
361                decompressed_data
362            };
363            (color_type, CrxImageData::RowEncoded(uncompressed))
364        };
365
366        Ok(CrxImage {
367            header,
368            color_type,
369            data,
370            compress_level: config.zlib_compression_level,
371            keep_original_bpp: config.circus_crx_keep_original_bpp,
372            zstd: config.circus_crx_zstd,
373            zstd_compression_level: config.zstd_compression_level,
374            row_type: config.circus_crx_mode.for_importing(),
375            canvas: config.circus_crx_canvas,
376        })
377    }
378
379    /// Whether to draw image on canvas if canvas's width and height are specified in image.
380    pub fn with_canvas(mut self, canvas: bool) -> Self {
381        self.canvas = canvas;
382        self
383    }
384
385    /// Draws another image on this image.
386    ///
387    /// Returns a new `ImageData` with the combined image.
388    pub fn draw_diff(&self, diff: &Self) -> Result<ImageData> {
389        let base_header = &self.header;
390        let diff_header = &diff.header;
391        let (img_width, img_height) =
392            if base_header.clips.is_empty() && diff_header.clips.is_empty() {
393                (
394                    (base_header.width + base_header.inner_x)
395                        .max(diff_header.width + diff_header.inner_x),
396                    (base_header.height + base_header.inner_y)
397                        .max(diff_header.height + diff_header.inner_y),
398                )
399            } else {
400                if base_header.clips.is_empty() {
401                    let clip = &diff_header.clips[0];
402                    (clip.img_width, clip.img_height)
403                } else {
404                    let clip = &base_header.clips[0];
405                    (clip.img_width, clip.img_height)
406                }
407            };
408        let base = self.export_image()?;
409        let mut nw = draw_on_canvas(
410            base,
411            img_width as u32,
412            img_height as u32,
413            base_header.inner_x as u32,
414            base_header.inner_y as u32,
415        )?;
416        draw_on_img(
417            &mut nw,
418            &diff.export_image()?,
419            diff_header.inner_x as u32,
420            diff_header.inner_y as u32,
421        )?;
422        Ok(nw)
423    }
424
425    fn decode_row0(
426        dst: &mut Vec<u8>,
427        mut dst_p: usize,
428        src: &[u8],
429        mut src_p: usize,
430        width: u16,
431        pixel_size: u8,
432    ) -> Result<usize> {
433        let mut prev_p = dst_p;
434        for _ in 0..pixel_size {
435            dst[dst_p] = src[src_p];
436            dst_p += 1;
437            src_p += 1;
438        }
439        let remaining = width - 1;
440        for _ in 0..remaining {
441            for _ in 0..pixel_size {
442                dst[dst_p] = src[src_p].overflowing_add(dst[prev_p]).0;
443                dst_p += 1;
444                src_p += 1;
445                prev_p += 1;
446            }
447        }
448        Ok(src_p)
449    }
450
451    fn decode_row1(
452        dst: &mut Vec<u8>,
453        mut dst_p: usize,
454        src: &[u8],
455        mut src_p: usize,
456        width: u16,
457        pixel_size: u8,
458        mut prev_row_p: usize,
459    ) -> Result<usize> {
460        for _ in 0..width {
461            for _ in 0..pixel_size {
462                dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
463                dst_p += 1;
464                src_p += 1;
465                prev_row_p += 1;
466            }
467        }
468        Ok(src_p)
469    }
470
471    fn decode_row2(
472        dst: &mut Vec<u8>,
473        mut dst_p: usize,
474        src: &[u8],
475        mut src_p: usize,
476        width: u16,
477        pixel_size: u8,
478        mut prev_row_p: usize,
479    ) -> Result<usize> {
480        for _ in 0..pixel_size {
481            dst[dst_p] = src[src_p];
482            dst_p += 1;
483            src_p += 1;
484        }
485        let remaining = width - 1;
486        for _ in 0..remaining {
487            for _ in 0..pixel_size {
488                dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
489                dst_p += 1;
490                src_p += 1;
491                prev_row_p += 1;
492            }
493        }
494        Ok(src_p)
495    }
496
497    fn decode_row3(
498        dst: &mut Vec<u8>,
499        mut dst_p: usize,
500        src: &[u8],
501        mut src_p: usize,
502        width: u16,
503        pixel_size: u8,
504        mut prev_row_p: usize,
505    ) -> Result<usize> {
506        let count = width - 1;
507        prev_row_p += pixel_size as usize;
508        for _ in 0..count {
509            for _ in 0..pixel_size {
510                dst[dst_p] = src[src_p].overflowing_add(dst[prev_row_p]).0;
511                dst_p += 1;
512                src_p += 1;
513                prev_row_p += 1;
514            }
515        }
516        for _ in 0..pixel_size {
517            dst[dst_p] = src[src_p];
518            dst_p += 1;
519            src_p += 1;
520        }
521        Ok(src_p)
522    }
523
524    fn decode_row4(
525        dst: &mut Vec<u8>,
526        dst_p: usize,
527        src: &[u8],
528        mut src_p: usize,
529        width: u16,
530        pixel_size: u8,
531    ) -> Result<usize> {
532        for offset in 0..pixel_size {
533            let mut dst_c = dst_p + offset as usize;
534            let mut remaining = width;
535            let value = src[src_p];
536            src_p += 1;
537            dst[dst_c] = value;
538            dst_c += pixel_size as usize;
539            remaining -= 1;
540            if remaining == 0 {
541                continue;
542            }
543            if value == src[src_p] {
544                src_p += 1;
545                let count = src[src_p] as u16;
546                src_p += 1;
547                remaining -= count;
548                for _ in 0..count {
549                    dst[dst_c] = value;
550                    dst_c += pixel_size as usize;
551                }
552            }
553            while remaining > 0 {
554                let value = src[src_p];
555                src_p += 1;
556                dst[dst_c] = value;
557                dst_c += pixel_size as usize;
558                remaining -= 1;
559                if remaining == 0 {
560                    break;
561                }
562                if value == src[src_p] {
563                    src_p += 1;
564                    let count = src[src_p] as u16;
565                    src_p += 1;
566                    remaining -= count;
567                    for _ in 0..count {
568                        dst[dst_c] = value;
569                        dst_c += pixel_size as usize;
570                    }
571                }
572            }
573        }
574        Ok(src_p)
575    }
576
577    fn read_v1_palette<T: Read>(reader: &mut T, raw_colors: usize) -> Result<Vec<u8>> {
578        if raw_colors == 0 {
579            return Err(anyhow::anyhow!("CRX v1 palette has zero colors"));
580        }
581        let color_size = if raw_colors == 0x0102 { 4usize } else { 3usize };
582        let mut colors = raw_colors;
583        if colors > 0x0100 {
584            colors = 0x0100;
585        }
586        let palette_size = colors
587            .checked_mul(color_size)
588            .ok_or_else(|| anyhow::anyhow!("CRX v1 palette size overflow"))?;
589        if palette_size == 0 {
590            return Err(anyhow::anyhow!("CRX v1 palette size is zero"));
591        }
592        let mut palette_raw = vec![0u8; palette_size];
593        reader.read_exact(&mut palette_raw)?;
594        let mut palette = Vec::with_capacity(colors * 3);
595        let mut pos = 0usize;
596        while pos < palette_raw.len() {
597            let r = palette_raw[pos];
598            let mut g = palette_raw[pos + 1];
599            let b = palette_raw[pos + 2];
600            if b == 0xFF && g == 0x00 && r == 0xFF {
601                g = 0xFF;
602            }
603            palette.push(r);
604            palette.push(g);
605            palette.push(b);
606            pos += color_size;
607        }
608        Ok(palette)
609    }
610
611    fn unpack_v1<T: Read>(reader: &mut T, output_len: usize) -> Result<Vec<u8>> {
612        const WINDOW_SIZE: usize = 0x10000;
613        const WINDOW_MASK: usize = WINDOW_SIZE - 1;
614        let mut window = vec![0u8; WINDOW_SIZE];
615        let mut win_pos: usize = 0;
616        let mut dst = vec![0u8; output_len];
617        let mut dst_pos = 0usize;
618        let mut flag: u16 = 0;
619        while dst_pos < output_len {
620            flag >>= 1;
621            if (flag & 0x100) == 0 {
622                let next = reader.read_u8()? as u16;
623                flag = next | 0xFF00;
624            }
625            if (flag & 1) != 0 {
626                let byte = reader.read_u8()?;
627                window[win_pos] = byte;
628                win_pos = (win_pos + 1) & WINDOW_MASK;
629                dst[dst_pos] = byte;
630                dst_pos += 1;
631            } else {
632                let control = reader.read_u8()?;
633                let (count, offset_value) = if control >= 0xC0 {
634                    let next = reader.read_u8()? as usize;
635                    let offset = (((control as usize) & 0x03) << 8) | next;
636                    let count = 4 + (((control as usize) >> 2) & 0x0F);
637                    (count, offset)
638                } else if (control & 0x80) != 0 {
639                    let mut offset = (control & 0x1F) as usize;
640                    let count = 2 + (((control as usize) >> 5) & 0x03);
641                    if offset == 0 {
642                        offset = reader.read_u8()? as usize;
643                    }
644                    (count, offset)
645                } else if control == 0x7F {
646                    let count = 2 + reader.read_u16()? as usize;
647                    let offset = reader.read_u16()? as usize;
648                    (count, offset)
649                } else {
650                    let offset = reader.read_u16()? as usize;
651                    let count = control as usize + 4;
652                    (count, offset)
653                };
654
655                let mut offset_pos = (win_pos.wrapping_sub(offset_value)) & WINDOW_MASK;
656                for _ in 0..count {
657                    if dst_pos >= output_len {
658                        break;
659                    }
660                    let value = window[offset_pos];
661                    offset_pos = (offset_pos + 1) & WINDOW_MASK;
662                    window[win_pos] = value;
663                    win_pos = (win_pos + 1) & WINDOW_MASK;
664                    dst[dst_pos] = value;
665                    dst_pos += 1;
666                }
667            }
668        }
669        Ok(dst)
670    }
671
672    fn decode_image(
673        dst: &mut Vec<u8>,
674        src: &[u8],
675        width: u16,
676        height: u16,
677        pixel_size: u8,
678        encode_type: &mut Vec<u8>,
679    ) -> Result<()> {
680        let mut src_p = 0;
681        let mut dst_p = 0;
682        let mut prev_row_p = 0;
683        for _ in 0..height {
684            let data = src[src_p];
685            encode_type.push(data);
686            src_p += 1;
687            match data {
688                0 => {
689                    src_p = Self::decode_row0(dst, dst_p, src, src_p, width, pixel_size)?;
690                }
691                1 => {
692                    src_p =
693                        Self::decode_row1(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
694                }
695                2 => {
696                    src_p =
697                        Self::decode_row2(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
698                }
699                3 => {
700                    src_p =
701                        Self::decode_row3(dst, dst_p, src, src_p, width, pixel_size, prev_row_p)?;
702                }
703                4 => {
704                    src_p = Self::decode_row4(dst, dst_p, src, src_p, width, pixel_size)?;
705                }
706                _ => {
707                    return Err(anyhow::anyhow!("Invalid row type: {}", data));
708                }
709            }
710            prev_row_p = dst_p;
711            dst_p += pixel_size as usize * width as usize;
712        }
713        Ok(())
714    }
715
716    fn encode_row0(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
717        let pixel_size = pixel_size as usize;
718        let mut src_p = y as usize * width as usize * pixel_size;
719        for _ in 0..pixel_size {
720            dst.push(src[src_p]);
721            src_p += 1;
722        }
723        for _ in 1..width {
724            for _ in 0..pixel_size {
725                dst.push(src[src_p].wrapping_sub(src[src_p - pixel_size]));
726                src_p += 1;
727            }
728        }
729    }
730
731    fn encode_row1(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
732        let pixel_size = pixel_size as usize;
733        let mut src_p = y as usize * width as usize * pixel_size;
734        let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
735        for _ in 0..width {
736            for _ in 0..pixel_size {
737                dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
738                src_p += 1;
739                prev_row_p += 1;
740            }
741        }
742    }
743
744    fn encode_row2(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
745        let pixel_size = pixel_size as usize;
746        let mut src_p = y as usize * width as usize * pixel_size;
747        let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size;
748        for _ in 0..pixel_size {
749            dst.push(src[src_p]);
750            src_p += 1;
751        }
752        for _ in 1..width {
753            for _ in 0..pixel_size {
754                dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
755                src_p += 1;
756                prev_row_p += 1;
757            }
758        }
759    }
760
761    fn encode_row3(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
762        let pixel_size = pixel_size as usize;
763        let mut src_p = y as usize * width as usize * pixel_size;
764        let mut prev_row_p = (y as usize - 1) * width as usize * pixel_size + pixel_size;
765        for _ in 0..width - 1 {
766            for _ in 0..pixel_size {
767                dst.push(src[src_p].wrapping_sub(src[prev_row_p]));
768                src_p += 1;
769                prev_row_p += 1;
770            }
771        }
772        for _ in 0..pixel_size {
773            dst.push(src[src_p]);
774            src_p += 1;
775        }
776    }
777
778    fn encode_row4(dst: &mut Vec<u8>, src: &[u8], width: u16, pixel_size: u8, y: u16) {
779        let pixel_size = pixel_size as usize;
780        let src_p = y as usize * width as usize * pixel_size;
781        for offset in 0..pixel_size {
782            let mut src_c = src_p + offset;
783            let mut remaining = width;
784            let value = src[src_c];
785            src_c += pixel_size;
786            dst.push(value);
787            remaining -= 1;
788            if remaining == 0 {
789                continue;
790            }
791            let mut count = 0;
792            loop {
793                if count as u16 >= remaining || count >= 255 || src[src_c] != value {
794                    break;
795                }
796                src_c += pixel_size;
797                count += 1;
798            }
799            if count > 0 {
800                dst.push(value);
801                dst.push(count);
802                remaining -= count as u16;
803            }
804            while remaining > 0 {
805                let value = src[src_c];
806                src_c += pixel_size;
807                dst.push(value);
808                remaining -= 1;
809                if remaining == 0 {
810                    break;
811                }
812                let mut count = 0;
813                loop {
814                    if count as u16 >= remaining || count >= 255 || src[src_c] != value {
815                        break;
816                    }
817                    src_c += pixel_size;
818                    count += 1;
819                }
820                if count > 0 {
821                    dst.push(value);
822                    dst.push(count);
823                    remaining -= count as u16;
824                }
825            }
826        }
827    }
828
829    fn encode_row_best(
830        dst: &mut Vec<u8>,
831        src: &[u8],
832        width: u16,
833        pixel_size: u8,
834        y: u16,
835    ) -> Result<()> {
836        let mut buf = Vec::with_capacity(width as usize * pixel_size as usize);
837        Self::encode_row0(&mut buf, src, width, pixel_size, y);
838        let mut compressed_len = {
839            let mut encoder =
840                flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
841            encoder.write_all(&buf)?;
842            let compressed_data = encoder.finish()?;
843            compressed_data.into_inner().len()
844        };
845        let mut buf_row_type = 0;
846        for row_type in 1..5u8 {
847            if y == 0 && row_type < 4 {
848                continue;
849            }
850            let mut newbuf = Vec::with_capacity(width as usize * pixel_size as usize);
851            match row_type {
852                1 => Self::encode_row1(&mut newbuf, src, width, pixel_size, y),
853                2 => Self::encode_row2(&mut newbuf, src, width, pixel_size, y),
854                3 => Self::encode_row3(&mut newbuf, src, width, pixel_size, y),
855                4 => Self::encode_row4(&mut newbuf, src, width, pixel_size, y),
856                _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
857            };
858            let new_compressed_len = {
859                let mut encoder =
860                    flate2::write::ZlibEncoder::new(MemWriter::new(), flate2::Compression::fast());
861                encoder.write_all(&newbuf)?;
862                let compressed_data = encoder.finish()?;
863                compressed_data.into_inner().len()
864            };
865            if new_compressed_len < compressed_len {
866                compressed_len = new_compressed_len;
867                buf = newbuf;
868                buf_row_type = row_type;
869            }
870        }
871        dst.push(buf_row_type);
872        dst.extend_from_slice(&buf);
873        Ok(())
874    }
875
876    fn encode_image_best(src: &[u8], width: u16, height: u16, pixel_size: u8) -> Result<Vec<u8>> {
877        let size = width as usize * height as usize * pixel_size as usize + height as usize;
878        let mut dst = Vec::with_capacity(size);
879        for y in 0..height {
880            Self::encode_row_best(&mut dst, src, width, pixel_size, y)?;
881        }
882        Ok(dst)
883    }
884
885    fn encode_image_fixed(
886        src: &[u8],
887        width: u16,
888        height: u16,
889        pixel_size: u8,
890        row_type: u8,
891    ) -> Result<Vec<u8>> {
892        let size = width as usize * height as usize * pixel_size as usize + height as usize;
893        let mut dst = Vec::with_capacity(size);
894        for y in 0..height {
895            let row_type = if y == 0 && row_type != 0 && row_type != 4 {
896                0
897            } else {
898                row_type
899            };
900            dst.push(row_type);
901            match row_type {
902                0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
903                1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
904                2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
905                3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
906                4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
907                _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
908            };
909        }
910        Ok(dst)
911    }
912
913    fn encode_image_origin(
914        src: &[u8],
915        width: u16,
916        height: u16,
917        pixel_size: u8,
918        row_type: &[u8],
919    ) -> Result<Vec<u8>> {
920        if row_type.len() != height as usize {
921            return Err(anyhow::anyhow!("Row type length does not match height"));
922        }
923        let size = width as usize * height as usize * pixel_size as usize + height as usize;
924        let mut dst = Vec::with_capacity(size);
925        for y in 0..height {
926            let row_type = row_type[y as usize];
927            dst.push(row_type);
928            match row_type {
929                0 => Self::encode_row0(&mut dst, src, width, pixel_size, y),
930                1 => Self::encode_row1(&mut dst, src, width, pixel_size, y),
931                2 => Self::encode_row2(&mut dst, src, width, pixel_size, y),
932                3 => Self::encode_row3(&mut dst, src, width, pixel_size, y),
933                4 => Self::encode_row4(&mut dst, src, width, pixel_size, y),
934                _ => return Err(anyhow::anyhow!("Invalid row type: {}", row_type)),
935            };
936        }
937        Ok(dst)
938    }
939
940    /// Creates a CRX image file from the given image data and writes it to the specified writer.
941    ///
942    /// * `data` - The input image data.
943    /// * `writer` - The writer to write the CRX image to.
944    /// * `config` - Extra configuration options.
945    pub fn create_image<T: Write + Seek>(
946        mut data: ImageData,
947        mut writer: T,
948        config: &ExtraConfig,
949    ) -> Result<()> {
950        let header = Header {
951            inner_x: 0,
952            inner_y: 0,
953            width: data.width as u16,
954            height: data.height as u16,
955            version: 2,
956            flags: 0x10, // Force add compressed data length
957            bpp: match data.color_type {
958                ImageColorType::Bgr => 0,
959                ImageColorType::Bgra => 1,
960                ImageColorType::Rgb => {
961                    convert_rgb_to_bgr(&mut data)?;
962                    0
963                }
964                ImageColorType::Rgba => {
965                    convert_rgba_to_bgra(&mut data)?;
966                    1
967                }
968                _ => {
969                    return Err(anyhow::anyhow!(
970                        "Unsupported color type: {:?}",
971                        data.color_type
972                    ));
973                }
974            },
975            mode: 0,
976            clips: Vec::new(),
977        };
978        let pixel_size = data.color_type.bpp(1) as u8;
979        if data.color_type == ImageColorType::Bgra && header.mode != 1 {
980            let alpha_flip = if header.mode == 2 { 0 } else { 0xFF };
981            for i in (0..data.data.len()).step_by(4) {
982                let b = data.data[i];
983                let g = data.data[i + 1];
984                let r = data.data[i + 2];
985                let a = data.data[i + 3];
986                data.data[i] = a ^ alpha_flip;
987                data.data[i + 1] = b;
988                data.data[i + 2] = g;
989                data.data[i + 3] = r;
990            }
991        }
992        let mode = config.circus_crx_mode.for_creating();
993        let encoded = if mode.is_best() {
994            Self::encode_image_best(&data.data, header.width, header.height, pixel_size)?
995        } else if let CircusCrxMode::Fixed(mode) = mode {
996            Self::encode_image_fixed(&data.data, header.width, header.height, pixel_size, mode)?
997        } else {
998            return Err(anyhow::anyhow!(
999                "Unsupported row type for creating: {:?}",
1000                mode
1001            ));
1002        };
1003        let compressed = if config.circus_crx_zstd {
1004            let mut encoder = zstd::Encoder::new(MemWriter::new(), config.zstd_compression_level)?;
1005            encoder.write_all(&encoded)?;
1006            let compressed_data = encoder.finish()?;
1007            compressed_data.into_inner()
1008        } else {
1009            let mut encoder = flate2::write::ZlibEncoder::new(
1010                MemWriter::new(),
1011                flate2::Compression::new(config.zlib_compression_level),
1012            );
1013            encoder.write_all(&encoded)?;
1014            let compressed_data = encoder.finish()?;
1015            compressed_data.into_inner()
1016        };
1017        writer.write_all(b"CRXG")?;
1018        header.pack(&mut writer, false, Encoding::Utf8, &None)?;
1019        writer.write_u32(compressed.len() as u32)?;
1020        writer.write_all(&compressed)?;
1021        Ok(())
1022    }
1023}
1024
1025impl Script for CrxImage {
1026    fn default_output_script_type(&self) -> OutputScriptType {
1027        OutputScriptType::Json
1028    }
1029
1030    fn default_format_type(&self) -> FormatOptions {
1031        FormatOptions::None
1032    }
1033
1034    fn is_image(&self) -> bool {
1035        true
1036    }
1037
1038    fn export_image(&self) -> Result<ImageData> {
1039        let width = usize::from(self.header.width);
1040        let height = usize::from(self.header.height);
1041        let mut img = match &self.data {
1042            CrxImageData::RowEncoded(encoded) => {
1043                let pixel_size = self.color_type.bpp(1) as usize;
1044                let row_bytes = pixel_size
1045                    .checked_mul(width)
1046                    .ok_or_else(|| anyhow::anyhow!("Image row size overflow"))?;
1047                let data_size = row_bytes
1048                    .checked_mul(height)
1049                    .ok_or_else(|| anyhow::anyhow!("Image buffer size overflow"))?;
1050                let mut data = vec![0u8; data_size];
1051                let mut encode_type = Vec::with_capacity(height);
1052                Self::decode_image(
1053                    &mut data,
1054                    encoded,
1055                    self.header.width,
1056                    self.header.height,
1057                    self.color_type.bpp(1) as u8,
1058                    &mut encode_type,
1059                )?;
1060                if self.color_type.bpp(1) == 4 && self.header.mode != 1 {
1061                    let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1062                    for chunk in data.chunks_mut(4) {
1063                        let a = chunk[0];
1064                        let b = chunk[1];
1065                        let g = chunk[2];
1066                        let r = chunk[3];
1067                        chunk[0] = b;
1068                        chunk[1] = g;
1069                        chunk[2] = r;
1070                        chunk[3] = a ^ alpha_flip;
1071                    }
1072                }
1073                ImageData {
1074                    width: self.header.width as u32,
1075                    height: self.header.height as u32,
1076                    depth: 8,
1077                    color_type: self.color_type,
1078                    data,
1079                }
1080            }
1081            CrxImageData::Direct(pixels) => {
1082                let mut data = pixels.clone();
1083                if self.color_type == ImageColorType::Bgra && self.header.mode != 1 {
1084                    let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1085                    for chunk in data.chunks_mut(4) {
1086                        let a = chunk[0];
1087                        let b = chunk[1];
1088                        let g = chunk[2];
1089                        let r = chunk[3];
1090                        chunk[0] = b;
1091                        chunk[1] = g;
1092                        chunk[2] = r;
1093                        chunk[3] = a ^ alpha_flip;
1094                    }
1095                }
1096                ImageData {
1097                    width: self.header.width as u32,
1098                    height: self.header.height as u32,
1099                    depth: 8,
1100                    color_type: self.color_type,
1101                    data,
1102                }
1103            }
1104            CrxImageData::IndexedV1 {
1105                pixels,
1106                stride,
1107                palette,
1108                palette_format,
1109                pixel_depth_bits,
1110            } => {
1111                let total_pixels = width
1112                    .checked_mul(height)
1113                    .ok_or_else(|| anyhow::anyhow!("Image dimensions overflow"))?;
1114                let mut indexed = Vec::with_capacity(total_pixels);
1115                for row in 0..height {
1116                    let start = row
1117                        .checked_mul(*stride)
1118                        .ok_or_else(|| anyhow::anyhow!("Row offset overflow"))?;
1119                    let end = start
1120                        .checked_add(width)
1121                        .ok_or_else(|| anyhow::anyhow!("Row slice overflow"))?;
1122                    if end > pixels.len() {
1123                        return Err(anyhow::anyhow!("CRX v1 indexed data is truncated"));
1124                    }
1125                    indexed.extend_from_slice(&pixels[start..end]);
1126                }
1127                let image = convert_index_palette_to_normal_bitmap(
1128                    &indexed,
1129                    *pixel_depth_bits,
1130                    palette,
1131                    *palette_format,
1132                    width,
1133                    height,
1134                )?;
1135                image
1136            }
1137        };
1138
1139        if self.canvas {
1140            let (img_width, img_height) = if self.header.clips.is_empty() {
1141                (self.header.width as u32, self.header.height as u32)
1142            } else {
1143                let clip = &self.header.clips[0];
1144                (clip.img_width as u32, clip.img_height as u32)
1145            };
1146            img = draw_on_canvas(
1147                img,
1148                img_width,
1149                img_height,
1150                self.header.inner_x as u32,
1151                self.header.inner_y as u32,
1152            )?;
1153        }
1154        Ok(img)
1155    }
1156
1157    fn import_image<'a>(
1158        &'a self,
1159        mut data: ImageData,
1160        _filename: &str,
1161        mut file: Box<dyn WriteSeek + 'a>,
1162    ) -> Result<()> {
1163        let mut color_type = match data.color_type {
1164            ImageColorType::Bgr => ImageColorType::Bgr,
1165            ImageColorType::Bgra => ImageColorType::Bgra,
1166            ImageColorType::Rgb => {
1167                convert_rgb_to_bgr(&mut data)?;
1168                ImageColorType::Bgr
1169            }
1170            ImageColorType::Rgba => {
1171                convert_rgba_to_bgra(&mut data)?;
1172                ImageColorType::Bgra
1173            }
1174            _ => {
1175                return Err(anyhow::anyhow!(
1176                    "Unsupported color type: {:?}",
1177                    data.color_type
1178                ));
1179            }
1180        };
1181        if data.width != self.header.width as u32 {
1182            return Err(anyhow::anyhow!(
1183                "Image width does not match: expected {}, got {}",
1184                self.header.width,
1185                data.width
1186            ));
1187        }
1188        if data.height != self.header.height as u32 {
1189            return Err(anyhow::anyhow!(
1190                "Image height does not match: expected {}, got {}",
1191                self.header.height,
1192                data.height
1193            ));
1194        }
1195        if data.depth != 8 {
1196            return Err(anyhow::anyhow!("Image depth must be 8, got {}", data.depth));
1197        }
1198        if data.color_type != self.color_type && self.keep_original_bpp {
1199            if self.color_type == ImageColorType::Bgr {
1200                convert_bgra_to_bgr(&mut data)?;
1201            } else if self.color_type == ImageColorType::Bgra {
1202                convert_bgr_to_bgra(&mut data)?;
1203            } else {
1204                return Err(anyhow::anyhow!(
1205                    "Unsupported color type for import: {:?}",
1206                    self.color_type
1207                ));
1208            }
1209            color_type = self.color_type;
1210        }
1211        let mut new_header = self.header.clone();
1212        new_header.bpp = match color_type {
1213            ImageColorType::Bgr => 0,
1214            ImageColorType::Bgra => 1,
1215            _ => return Err(anyhow::anyhow!("Unsupported color type: {:?}", color_type)),
1216        };
1217        if new_header.version == 1 {
1218            new_header.version = 2; // Upgrade to version 2
1219        }
1220        new_header.flags |= 0x10; // Force add compressed data length
1221        let pixel_size = color_type.bpp(1) as u8;
1222        if color_type == ImageColorType::Bgra && self.header.mode != 1 {
1223            let alpha_flip = if self.header.mode == 2 { 0 } else { 0xFF };
1224            for i in (0..data.data.len()).step_by(4) {
1225                let b = data.data[i];
1226                let g = data.data[i + 1];
1227                let r = data.data[i + 2];
1228                let a = data.data[i + 3];
1229                data.data[i] = a ^ alpha_flip;
1230                data.data[i + 1] = b;
1231                data.data[i + 2] = g;
1232                data.data[i + 3] = r;
1233            }
1234        }
1235        let encoded = if self.row_type.is_origin() && self.data.is_row_encoded() {
1236            let mut row_type = Vec::with_capacity(self.header.height as usize);
1237            let pixel_size_bytes = self.color_type.bpp(1) as usize;
1238            let row_len = pixel_size_bytes
1239                .checked_mul(self.header.width as usize)
1240                .ok_or_else(|| anyhow::anyhow!("Row length overflow"))?
1241                .checked_add(1)
1242                .ok_or_else(|| anyhow::anyhow!("Row length overflow"))?;
1243            let buffer = match &self.data {
1244                CrxImageData::RowEncoded(buf) => buf,
1245                _ => {
1246                    return Err(anyhow::anyhow!(
1247                        "Original row type information is unavailable"
1248                    ));
1249                }
1250            };
1251            let mut cur_pos = 0usize;
1252            for _ in 0..self.header.height {
1253                if cur_pos >= buffer.len() {
1254                    return Err(anyhow::anyhow!("Row type offset exceeds buffer length"));
1255                }
1256                let cur_row_type = buffer[cur_pos];
1257                row_type.push(cur_row_type);
1258                let true_row_len = if cur_row_type < 4 {
1259                    row_len
1260                } else {
1261                    let mut offset = cur_pos + 1;
1262                    for _ in 0..pixel_size {
1263                        let mut remaing = self.header.width;
1264                        let value = buffer[offset];
1265                        offset += 1;
1266                        remaing -= 1;
1267                        if remaing == 0 {
1268                            continue;
1269                        }
1270                        if value == buffer[offset] {
1271                            offset += 1;
1272                            let count = buffer[offset] as u16;
1273                            offset += 1;
1274                            remaing = remaing
1275                                .checked_sub(count)
1276                                .ok_or_else(|| anyhow::anyhow!("Row run-length overflow"))?;
1277                        }
1278                        while remaing > 0 {
1279                            let value = buffer[offset];
1280                            offset += 1;
1281                            remaing -= 1;
1282                            if remaing == 0 {
1283                                break;
1284                            }
1285                            if value == buffer[offset] {
1286                                offset += 1;
1287                                let count = buffer[offset] as u16;
1288                                offset += 1;
1289                                remaing = remaing
1290                                    .checked_sub(count)
1291                                    .ok_or_else(|| anyhow::anyhow!("Row run-length overflow"))?;
1292                            }
1293                        }
1294                    }
1295                    offset - cur_pos
1296                };
1297                cur_pos = cur_pos
1298                    .checked_add(true_row_len)
1299                    .ok_or_else(|| anyhow::anyhow!("Row type offset overflow"))?;
1300            }
1301            Self::encode_image_origin(
1302                &data.data,
1303                new_header.width,
1304                new_header.height,
1305                pixel_size,
1306                &row_type,
1307            )?
1308        } else if self.row_type.is_best()
1309            || (self.row_type.is_origin() && !self.data.is_row_encoded())
1310        {
1311            Self::encode_image_best(&data.data, new_header.width, new_header.height, pixel_size)?
1312        } else if let CircusCrxMode::Fixed(mode) = self.row_type {
1313            Self::encode_image_fixed(
1314                &data.data,
1315                new_header.width,
1316                new_header.height,
1317                pixel_size,
1318                mode,
1319            )?
1320        } else {
1321            return Err(anyhow::anyhow!(
1322                "Unsupported row type for import: {:?}",
1323                self.row_type
1324            ));
1325        };
1326        let compressed = if self.zstd {
1327            let mut encoder = zstd::Encoder::new(MemWriter::new(), self.zstd_compression_level)?;
1328            encoder.write_all(&encoded)?;
1329            let compressed_data = encoder.finish()?;
1330            compressed_data.into_inner()
1331        } else {
1332            let mut encoder = flate2::write::ZlibEncoder::new(
1333                MemWriter::new(),
1334                flate2::Compression::new(self.compress_level),
1335            );
1336            encoder.write_all(&encoded)?;
1337            let compressed_data = encoder.finish()?;
1338            compressed_data.into_inner()
1339        };
1340        file.write_all(b"CRXG")?;
1341        new_header.pack(&mut file, false, Encoding::Utf8, &None)?;
1342        file.write_u32(compressed.len() as u32)?;
1343        file.write_all(&compressed)?;
1344        Ok(())
1345    }
1346}
1347
1348fn draw_on_img(base: &mut ImageData, diff: &ImageData, left: u32, top: u32) -> Result<()> {
1349    if base.color_type != diff.color_type {
1350        return Err(anyhow::anyhow!(
1351            "Color types do not match: {:?} vs {:?}",
1352            base.color_type,
1353            diff.color_type
1354        ));
1355    }
1356    let bpp = base.color_type.bpp(1) as usize;
1357    let base_stride = base.width as usize * bpp;
1358    let diff_stride = diff.width as usize * bpp;
1359
1360    for y in 0..diff.height {
1361        let base_y = top + y;
1362        if base_y >= base.height {
1363            continue; // Skip if the base image is not tall enough
1364        }
1365
1366        for x in 0..diff.width {
1367            let base_x = left + x;
1368            if base_x >= base.width {
1369                continue; // Skip if the base image is not wide enough
1370            }
1371
1372            let base_index = (base_y as usize * base_stride) + (base_x as usize * bpp);
1373            let diff_index = (y as usize * diff_stride) + (x as usize * bpp);
1374
1375            let diff_pixel = &diff.data[diff_index..diff_index + bpp];
1376            let base_pixel_orig = base.data[base_index..base_index + bpp].to_vec();
1377            let mut b = base_pixel_orig[0];
1378            let mut g = base_pixel_orig[1];
1379            let mut r = base_pixel_orig[2];
1380            wrapping! {
1381                b += diff_pixel[0];
1382                g += diff_pixel[1];
1383                r += diff_pixel[2];
1384            }
1385            base.data[base_index] = b;
1386            base.data[base_index + 1] = g;
1387            base.data[base_index + 2] = r;
1388            if bpp == 4 {
1389                let mut a = base_pixel_orig[3];
1390                wrapping! {
1391                    a -= diff_pixel[3];
1392                }
1393                base.data[base_index + 3] = a;
1394            }
1395        }
1396    }
1397    Ok(())
1398}